use std::{
    collections::BTreeMap,
    fmt::Display,
};

use anyhow::{anyhow, Error, Result};

#[derive(Debug)]
pub enum BencodeEntity {
    String(Vec<u8>),
    Integer(i64),
    List(Vec<BencodeEntity>),
    Dict(BTreeMap<String, BencodeEntity>),
}

fn decode_string(value: &[u8]) -> (Result<Vec<u8>>, &[u8]) {
    let mut len = String::new();
    let mut iter = value.iter();
    while let Some(c) = iter.next() {
        if c.is_ascii_digit() {
            len.push(*c as char)
        } else if *c == b':' {
            break;
        } else {
            return (
                Err(anyhow!("解析string遇到了错误的字符")),
                &value[len.len()..],
            );
        }
    }
    let i_len = len.parse().unwrap();
    if i_len + len.len() + 1 > value.len() {
        return (Err(anyhow!("格式错误：没有足够长度的字符串")), value);
    }
    let s: Vec<u8> = iter.take(i_len).map(|x| *x).collect();
    (Ok(s), &value[i_len + len.len() + 1..])
}

fn decode_integer(value: &[u8]) -> (Result<i64>, &[u8]) {
    let mut end: usize = 0;
    for i in 1..value.len() {
        if value[i] == b'e' {
            end = i;
            break;
        }
    }
    let i: i64 = String::from_utf8_lossy(&value[1..end]).parse().unwrap();

    (Ok(i), &value[end + 1..])
}

fn decode(value: &[u8]) -> (Result<BencodeEntity>, &[u8]) {
    if let Some(ch) = value.first() {
        match ch {
            b'0'..=b'9' => {
                let (v, new_rest) = decode_string(value);
                match v {
                    Ok(s) => (Ok(BencodeEntity::String(s)), new_rest),
                    Err(e) => (Err(e), new_rest),
                }
            }
            b'i' => {
                let (v, new_rest) = decode_integer(value);
                match v {
                    Ok(i) => (Ok(BencodeEntity::Integer(i)), new_rest),
                    Err(e) => (Err(e), new_rest),
                }
            }
            b'l' => {
                let mut rest = &value[1..];
                let mut res: Vec<BencodeEntity> = Vec::new();
                while let Some(f) = rest.first() {
                    if *f == b'e' {
                        rest = &rest[1..];
                        break;
                    }
                    let (entity_result, new_rest) = decode(rest);
                    match entity_result {
                        Ok(entity) => {
                            res.push(entity);
                            rest = new_rest;
                        }
                        Err(e) => {
                            return (Err(e), new_rest);
                        }
                    }
                }
                return ((Ok(BencodeEntity::List(res))), rest);
            }
            b'd' => {
                let mut rest = &value[1..];
                let mut hash_map = BTreeMap::<String, BencodeEntity>::new();
                while let Some(f) = rest.first() {
                    if *f == b'e' {
                        rest = &rest[1..];
                        break;
                    }
                    let (key_string, new_rest) = decode_string(rest);
                    rest = new_rest;
                    let (val, new_rest) = decode(rest);
                    match val {
                        Ok(en) => {
                            if let Ok(key) = key_string {
                                hash_map.insert(String::from_utf8_lossy(&key).into(), en);
                            }
                        }
                        Err(e) => return (Err(e), rest),
                    }
                    rest = new_rest;
                }
                (Ok(BencodeEntity::Dict(hash_map)), rest)
            }
            _ => {
                return (Err(anyhow!("不支持的格式")), value);
            }
        }
    } else {
        return (Err(anyhow!("不支持的格式")), value);
    }
}

impl TryFrom<&[u8]> for BencodeEntity {
    type Error = Error;

    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
        decode(value).0
    }
}

impl Display for BencodeEntity {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            BencodeEntity::String(s) => {
                write!(f, "\"{}\"", String::from_utf8_lossy(s))
            }
            BencodeEntity::Integer(i) => {
                write!(f, "{}", i)
            }
            BencodeEntity::List(l) => {
                let mut vec_str = Vec::<String>::new();
                for e in l {
                    vec_str.push(e.to_string());
                }
                write!(f, "[{}]", vec_str.join(", "))
            }
            BencodeEntity::Dict(d) => {
                let mut dict_str = Vec::<String>::new();
                for en in d.keys() {
                    let i = d.get(en).unwrap();
                    let item = format!("\"{}\" => {}", en, i);
                    dict_str.push(item);
                }
                write!(f, "{{{}}}", dict_str.join(", "))
            }
        }
    }
}

fn encode_string(str: &Vec<u8>) -> Vec<u8> {
    let res: Vec<u8> = Vec::new();
    res.iter()
        .chain(str.len().to_string().as_bytes())
        .chain(&[b':'][..])
        .chain(str.as_slice())
        .copied()
        .collect()
}

fn encode_integer(i: i64) -> Vec<u8> {
    let res = Vec::new();
    res.iter()
        .chain(&[b'i'][..])
        .chain(i.to_string().as_bytes())
        .chain(&[b'e'][..])
        .copied()
        .collect()
}

fn encode_list(list: &Vec<BencodeEntity>) -> Vec<u8> {
    let mut res = Vec::new();
    res.push(b'l');
    for ben in list {
        res.append(&mut _encode(ben));
    }
    res.push(b'e');
    return res;
}

fn encode_dict(dict: &BTreeMap<String, BencodeEntity>) -> Vec<u8> {
    let mut res = Vec::new();
    res.push(b'd');
    for ben in dict.keys() {
        res.append(encode_string(&Vec::from(ben.as_bytes())).as_mut());
        res.append(_encode(dict.get(ben).unwrap()).as_mut());
    }
    res.push(b'e');
    return res;
}

fn _encode(ben: &BencodeEntity) -> Vec<u8> {
    match ben {
        BencodeEntity::String(s) => encode_string(s),
        BencodeEntity::Integer(i) => encode_integer(*i),
        BencodeEntity::List(list) => encode_list(list),
        BencodeEntity::Dict(dict) => encode_dict(dict),
    }
}

impl BencodeEntity {
    pub fn encode(&self) -> Vec<u8> {
        _encode(self)
    }
}

#[cfg(test)]
mod test {
    use sha1::{Digest, Sha1};

    use super::BencodeEntity;

    #[test]
    fn test_str() {
        let input = "5:hello";
        let ben: BencodeEntity = BencodeEntity::try_from(input.as_bytes()).unwrap();
        assert_eq!(ben.to_string(), "\"hello\"".to_string());
        let input3 = "12:hello,world!";
        let ben3: BencodeEntity = BencodeEntity::try_from(input3.as_bytes()).unwrap();
        assert_eq!(ben3.to_string(), "\"hello,world!\"".to_string());
        let input2 = "15:hello";
        let ben2 = BencodeEntity::try_from(input2.as_bytes());
        assert!(ben2.is_err());
        let input4 = "0:";
        let ben4: BencodeEntity = BencodeEntity::try_from(input4.as_bytes()).unwrap();
        assert_eq!(ben4.to_string(), "\"\"".to_string());
        let input5 = "-2:";
        let ben5 = BencodeEntity::try_from(input5.as_bytes());
        assert!(ben5.is_err());
    }

    #[test]
    fn test_integer() {
        let input = "i32e";
        let ben = BencodeEntity::try_from(input.as_bytes()).unwrap();
        assert_eq!(ben.to_string(), "32".to_string());
        let input2 = "i32121212e";
        let ben2 = BencodeEntity::try_from(input2.as_bytes()).unwrap();
        assert_eq!(ben2.to_string(), "32121212".to_string());
        let input3 = "i-12345e";
        let ben3 = BencodeEntity::try_from(input3.as_bytes()).unwrap();
        assert_eq!(ben3.to_string(), "-12345".to_string());
        let input4 = "i0e";
        let ben4 = BencodeEntity::try_from(input4.as_bytes()).unwrap();
        assert_eq!(ben4.to_string(), "0".to_string());
    }

    #[test]
    fn test_list() {
        let input = "li123e3:123e";
        let ben = BencodeEntity::try_from(input.as_bytes());
        assert!(ben.is_ok());
        println!("{}", ben.unwrap())
    }

    #[test]
    fn test_list2() {
        let input = "li12eli33e2:33ee";
        let ben = BencodeEntity::try_from(input.as_bytes());
        println!("{}", ben.unwrap())
    }
    #[test]
    fn test_list3() {
        let input = "d5:hello5:worlde";
        let ben = BencodeEntity::try_from(input.as_bytes());
        println!("{}", ben.unwrap());
        let input2 = "d3:cow3:moo4:spam4:eggse";
        let ben2 = BencodeEntity::try_from(input2.as_bytes());
        println!("{}", ben2.unwrap());
        let input3 = "d4:listli32e5:helloe4:info4:infoe";
        let ben3 = BencodeEntity::try_from(input3.as_bytes());
        println!("{}", ben3.unwrap());
        let input4 = "d4:listli32e5:helloe4:dictd3:extli32ei64eeee";
        let ben4 = BencodeEntity::try_from(input4.as_bytes());
        println!("{}", ben4.unwrap())
    }
    #[test]
    fn test_file() {
        let torrent = std::fs::read("1.torrent").unwrap();
        let ben = BencodeEntity::try_from(torrent.as_slice()).unwrap();
        std::fs::write("1.torrent.txt", ben.to_string()).unwrap();
    }

    #[test]
    fn test_files() {
        let torrent = std::fs::read("peers_list.txt").unwrap();
        let ben = BencodeEntity::try_from(torrent.as_slice()).unwrap();
        std::fs::write("peers_list1.txt", ben.to_string()).unwrap();
    }

    #[test]
    fn test_extrac_info() {
        let torrent = std::fs::read("ubuntu-24.04-desktop-amd64.iso (1).torrent").unwrap();
        let ben = BencodeEntity::try_from(torrent.as_slice()).unwrap();
        if let BencodeEntity::Dict(d) = ben {
            if let Some(info_dict) = d.get("info") {
                let info_string = info_dict.encode();
                std::fs::write(
                    "ubuntu-24.04-desktop-amd64.iso (1).torrent.txt",
                    &info_string,
                )
                .unwrap();

                // create a Sha1 object
                let mut hasher = Sha1::new();

                // process input message
                hasher.update(info_string);

                // acquire hash digest in the form of GenericArray,
                // which in this case is equivalent to [u8; 20]
                let result = hasher.finalize();
                let s = result
                    .iter()
                    .map(|b| format!("{:02x}", b))
                    .collect::<Vec<String>>()
                    .join("");
                println!("{:?}", s);
                println!("{:?}", result);
            }
        }
    }

    #[test]
    fn test_files1() {
        // create a Sha1 object
        let mut hasher = Sha1::new();
        let torrent = std::fs::read("xubuntu-24.04-minimal-amd64.iso.torrent").unwrap();
        // process input message
        hasher.update(torrent.as_slice());

        // acquire hash digest in the form of GenericArray,
        // which in this case is equivalent to [u8; 20]
        let result = hasher.finalize();
        let s = result
            .iter()
            .map(|b| format!("{:02x}", b))
            .collect::<Vec<String>>()
            .join("");

        println!("{}", s)
    }
}
